<?php

 /**
  * @package JooS
  * @subpackage Syntax
  */
  
  final class JooS_Syntax_Compiler {
    /**
     * @var JooS_Syntax_Abstract
     */
    private $_syntax = null;

    /**
     * @var string
     */
    private $_expr = null;

    /**
     * @var int
     */
    private $_exprPos = null;

    /**
     * @var int
     */
    private $_exprLength = null;

    /**
     * @param JooS_Syntax_Abstract $syntax
     */
    private function __construct(JooS_Syntax_Abstract $syntax) {
      $this->_syntax = $syntax;
      $this->importSyntax($syntax);
    }

    /**
     * @param JooS_Syntax_Abstract $syntax
     * @return JooS_Syntax_Compiler
     */
    public function importSyntax(JooS_Syntax_Abstract $syntax) {
      $syntax->setParser($this);
      return $this;
    }

    /**
     * @param string $name
     * @return JooS_Syntax_Abstract
     */
    public function createSyntax($name) {
      require_once "JooS/Loader.php";

      $className = JooS_Loader::getClassName("JooS_Syntax", $name, true);

      if (JooS_Loader::loadClass($className)) {
        $syntax = new $className();
        if ($syntax instanceOf JooS_Syntax_Abstract) {
          $this->importSyntax($syntax);
          return $syntax;
        }
      }
      self::throwException("Class $className was not found");
    }

    /**
     * @param string $expr
     * @return bool
     */
    public function compile($expr) {
      $this->_expr = $expr;
      $this->_exprPos = 0;
      $this->_exprLength = strlen($expr);

      try {
        $compiled = $this->_syntax->compileSyntax();
        return $this->_exprPos === $this->_exprLength ? $compiled : false;
      }
      catch (JooS_Syntax_Exception $e) {
        return false;
      }
    }

    /**
     * @param int $count
     * @param bool $shift
     * @return string
     */
    public function substr($count, $shift = false) {
      if ($this->shiftCheck($count)) {
        $Chars = substr($this->_expr, $this->_exprPos, $count);
        if ($shift)
          $this->shiftPos($count);
        return $Chars;
      }
      else
        return false;
    }

    /**
     * @return int
     */
    public function getPos() {
      return $this->_exprPos;
    }

    /**
     * @param int $exprPos
     */
    public function setPos($exprPos) {
      $this->_exprPos = $exprPos;
    }

    /**
     * @param int $count
     * @return bool
     */
    private function shiftCheck($count) {
      return $this->_exprPos + $count <= $this->_exprLength ? true : false;
    }

    /**
     * @param int $count
     */
    private function shiftPos($count) {
      $this->_exprPos += $count;
    }

    /**
     * @param JooS_Syntax_Abstract $syntax
     * @return JooS_Syntax_Compiler
     */
    public static function newInstance(JooS_Syntax_Abstract $syntax) {
      return new self($syntax);
    }

    /**
     * @param string $Message
     * @param int $Code 
     * @throws JooS_Syntax_Exception
     */
    protected static function throwException($Message, $Code = 0) {
      require_once "JooS/Syntax/Exception.php";

      throw new JooS_Syntax_Exception($Message, $Code);
    }
  }
